﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using iNet.Tasks;

namespace iNet.Media.Video
{
	/// <summary>
	/// 支援 ISO base media file format 的影片解碼工作。
	/// </summary>
	public class IsoBaseVideoDecodeTask : BaseVideoDecodeTask
	{
		#region 建構子
		/// <summary>
		/// 初始化新的 IsoBaseVideoDecodeTask 執行個體。
		/// </summary>
		/// <param name="decodingFormat">解碼的影音容器格式。</param>
		public IsoBaseVideoDecodeTask(IMediaFormat decodingFormat)
			: base(decodingFormat)
		{ }
		/// <summary>
		/// 初始化新的 IsoBaseVideoDecodeTask 執行個體。
		/// </summary>
		/// <param name="decodingFormat">解碼的影音容器格式。</param>
		/// <param name="source">影片的儲存來源。</param>
		public IsoBaseVideoDecodeTask(IMediaFormat decodingFormat, MediaStorage source)
			: base(decodingFormat, source)
		{ }
		#endregion
		#region Decode
		/// <summary>
		/// 解碼影片。
		/// </summary>
		/// <param name="version">目前的工作版本。</param>
		/// <param name="source">影片儲存來源。</param>
		/// <param name="mediaDecodeReqs">媒體解碼請求。</param>
		/// <param name="videoDecodeReqs">影片解碼請求。</param>
		/// <returns>True 表示解碼成功，False 表示失敗。</returns>
		protected override bool Decode(TaskVersion version, MediaStorage source, MediaDecodeRequests mediaDecodeReqs, VideoDecodeRequests videoDecodeReqs)
		{
			//解碼影片資訊
			List<IbmfMetadataItem> metadataItems = null;
			if (((videoDecodeReqs & (VideoDecodeRequests.DecodeBitRate | Video.VideoDecodeRequests.DecodeVideoFrameRate)) != 0) || (mediaDecodeReqs & Media.MediaDecodeRequests.DecodeMetadata) != 0)
			{
				Stream stream;
				if (!this.GetMediaStorageStream(version, source, MediaStorageStreamFlags.Readable, 50, out stream))
					throw new MediaDecodeFailedException(MediaDecodeFailedReason.CannotAccessStream);
				try
				{
					bool skipInnerBoxes = false;
					bool readNextBox = true;
					IsoBaseMediaFileReader reader = new IsoBaseMediaFileReader(stream);
					while (readNextBox && reader.ReadNextBox(skipInnerBoxes))
					{
						skipInnerBoxes = false;
						switch (reader.CurrentBoxTypeString)
						{
							case "mdat":
								readNextBox = false;
								break;
							case "mdia":
								skipInnerBoxes = true;
								this.DecodeMdiaBox(version, reader.ReadBoxData());
								break;
							case "udta":
								skipInnerBoxes = true;
								this.DecodeUdtaBox(version, reader.ReadBoxData(), ref metadataItems);
								break;
						}
					}
				}
				finally
				{
					this.ReleaseMediaStorageStream(version, source, stream);
				}
				videoDecodeReqs &= ~(VideoDecodeRequests.DecodeBitRate | Video.VideoDecodeRequests.DecodeVideoFrameRate);
			}

			//儲存中繼資料
			if (metadataItems != null && metadataItems.Count > 0)
				this.SetDecodedMetadata(version, new IsoBaseMediaFileMetadata(metadataItems, true));

			//解碼剩餘部分
			return base.Decode(version, source, mediaDecodeReqs, videoDecodeReqs);
		}
		#endregion
		#region DecodeIlstBox
		void DecodeIlstBox(TaskVersion version, byte[] boxData, List<IbmfMetadataItem> metadataItems)
		{
			//確認參數
			if (boxData == null)
				return;

			//取得中繼資料
			using (MemoryStream stream = new MemoryStream(boxData))
			{
				IsoBaseMediaFileReader reader = new IsoBaseMediaFileReader(stream);
				while (reader.ReadNextBox(true))
				{
					try
					{
						metadataItems.Add(AppleIbmfMetadataItem.FromRawData(reader.CurrentBoxType.Value, reader.ReadBoxData()));
					}
					catch
					{ }
				}
			}
		}
		#endregion
		#region DecodeMdiaBox
		void DecodeMdiaBox(TaskVersion version, byte[] boxData)
		{
			//確認參數
			if (boxData == null)
				return;

			//解碼內容
			string headerBoxType = null;
			uint? timeScale = null;
			ulong? duration = null;
			using (MemoryStream stream = new MemoryStream(boxData))
			{
				IsoBaseMediaFileReader reader = new IsoBaseMediaFileReader(stream);
				while (reader.ReadNextBox(false))
				{
					switch (reader.CurrentBoxTypeString)
					{
						case "mdhd":
							{
								byte[] data = reader.ReadBoxData();
								if (data != null && data.Length > 0)
								{
									if (data[0] == 0)
									{
										if (data.Length >= 20)
										{
											timeScale = BitConverter.BigEndian.ToUInt32(data, 12);
											duration = BitConverter.BigEndian.ToUInt32(data, 16);
										}
									}
									else if (data[0] == 1)
									{
										if (data.Length >= 32)
										{
											timeScale = BitConverter.BigEndian.ToUInt32(data, 20);
											duration = BitConverter.BigEndian.ToUInt64(data, 24);
										}
									}
								}
							}
							break;
						case "smhd":
						case "vmhd":
							headerBoxType = reader.CurrentBoxTypeString;
							break;
						case "nmhd":
						case "hmhd":
							return;
					}
				}
			}

			//分析資料
			if (headerBoxType == "vmhd")
			{
				//取得 FPS
				if (timeScale != null)
					this.SetDecodedVideoFrameRate(version, (timeScale.Value / 1000.0));
			}
		}
		#endregion
		#region DecodeUdtaBox
		void DecodeUdtaBox(TaskVersion version, byte[] boxData, ref List<IbmfMetadataItem> metadataItems)
		{
			//確認參數
			if (boxData == null)
				return;

			//解碼內容
			metadataItems = new List<IbmfMetadataItem>();
			using (MemoryStream stream = new MemoryStream(boxData))
			{
				IsoBaseMediaFileReader reader = new IsoBaseMediaFileReader(stream);
				bool skipInnerBoxes = false;
				while (reader.ReadNextBox(skipInnerBoxes))
				{
					skipInnerBoxes = false;
					switch (reader.CurrentBoxTypeString)
					{
						case "ilst":
							skipInnerBoxes = true;
							this.DecodeIlstBox(version, reader.ReadBoxData(), metadataItems);
							break;
						case "Xtra":
							try
							{
								metadataItems.Add(MicrosoftIbmfMetadataItem.FromRawData(reader.ReadBoxData()));
							}
							catch
							{ }
							break;
					}
				}
			}
		}
		#endregion
	}
}
